#####################################################
#  		Python Port Scanner		    #
#####################################################
#		  Vallee Cedric			    #
#		    17/12/2011			    #
#	    Network and Systems Security 	    #
#####################################################
# In bash nc -vvz 192.168.0.1 79-81 2>&1 | grep open

import socket, subprocess
import sys
import threading
from optparse import OptionParser 

MAX_THREADS = 50

#ipRange generator from startAddr to endAddr
# startAddr : must be a standard ip : example 127.0.0.1
# endAddr : just need the last digit : 2.45 means from 127.0.0.1 to 127.0.2.45
def ipAddrRange(startAddr, endAddr):
	def incrAddr(addrList):
		addrList[3] += 1
		for i in (3,2,1):
			if addrList[i] == 256:
				addrList[i] = 0
				addrList[i-1] += 1

	def asString(addrList):
		return ".".join(map(str,addrList))

	startAddrList = map(int,startAddr.split("."))
	endAddrList = map(int,endAddr.split("."))

	if len(endAddrList)<4:
		for i in range(4-len(endAddrList)):
			endAddrList.insert(i,startAddrList[i])

	curAddrList = startAddrList[:]
	yield asString(curAddrList)
	for i in range(4):
		while curAddrList[i] < endAddrList[i]:
			incrAddr(curAddrList)
			yield asString(curAddrList)

#Threaded port's scanner
class Scanner(threading.Thread):
    def __init__(self, host, port):
        threading.Thread.__init__(self)
        # host and port
        self.host = host
        self.port = port
        # build up the socket obj
        self.sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def run(self):
        try:
		# connect to the given host:port
		self.sd.connect((self.host, self.port))
		print "%s:%d OPEN" % (self.host, self.port)
		self.sd.close()
        except:
		#print "%s:%d Closed" % (self.host, self.port)
		pass

#General scanner
class Scan:
	def __init__(self, host,start,end) :
		# start port and end port
		self.start, self.stop = start, end
		self.ipfin = ""

		# host resolution
		#Resolve the arguments like "127.0.0.1-2.45:78-85"
		#which means analyse any target from 127.0.0.1 to 127.0.2.45 from the 78 port to the 85
		if ':' in host:
			self.host = host.split(':')[0]
			self.ipfin = self.host
			if '-' in host.split(':')[1]:	
				self.start= int(host.split(':')[1].split('-')[0])
				self.stop= int(host.split(':')[1].split('-')[1])
			else :
				self.start = int(host.split(':')[1])
				self.stop = int(host.split(':')[1])
		else :
			self.host = host
			self.ipfin = host
		scan = "Scan of " + self.host
		if '-' in self.host :
			self.ipfin= self.host.split('-')[1]
			self.host= self.host.split('-')[0]
			scan = "Scan from " + self.host + " to " + self.ipfin	
		scan += "\nPorts '%d' to '%d'" %(self.start,self.stop)

		#beginning of the scan, target by target
		print scan
		for addr in ipAddrRange(self.host,self.ipfin):
			#We test if the target is alive by pinging it, icmp request
			ping = subprocess.Popen(
			    ["ping", "-c", "1", addr],
			    stdout = subprocess.PIPE,
			    stderr = subprocess.PIPE
			)
			out, error = ping.communicate()
			#if the host is awake then we scan
			if "1 received" in out:
				print "self scan lance %s"%addr
				self.scan(addr, self.start, self.stop)
			else:
				print "hostname '%s' unknown" % addr

	def scan(self, host, start, stop):
		self.port = start
		while self.port <= stop:
			# we start as much thread as possible
			# each one try one port
			# During the answer's delay, we can do other actions like trying an other port
			if threading.activeCount() < MAX_THREADS:
				Scanner(host, self.port).start()
				self.port += 1
        


def main():
	#we parse the options and arguments of the script
	parser = OptionParser(usage="usage: %prog [options] target",
                          version="%prog 1.0")
	parser.add_option("-s", "--start", dest="pstart",type="int", help ="Starting value of the port scanning",default="1") 
	parser.add_option("-e", "--end", dest="pend",type="int", help ="Ending value of the port scanning",default="1024")
	
	(options, args) = parser.parse_args()

	if len(args)==1 :
   		Scan(args[0],options.pstart,options.pend)
	else:
		parser.error("wrong number of arguments")
		
if __name__ == "__main__":
	main()
